// $codepro.audit.disable useEquals
/*
 * Records package
 */
package edu.gatech.oad.records;



import edu.gatech.oad.doctorsorder.DoctorsOrder;
import edu.gatech.oad.doctorsorder.LabOrder;
import edu.gatech.oad.doctorsorder.LabOrderPanel;
import edu.gatech.oad.doctorsorder.Prescription;
import edu.gatech.oad.doctorsorder.PrescriptionPanel;
import edu.gatech.oad.ui.UserPanel;
import edu.gatech.oad.user.AccountsController;
import edu.gatech.oad.user.Doctor;
import edu.gatech.oad.user.Nurse;
import edu.gatech.oad.user.Patient;
import edu.gatech.oad.user.User;
import edu.gatech.oad.user.Receptionist;
import edu.gatech.oad.user.SystemAdministrator;

import javax.swing.DefaultListModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.BoxLayout;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;

import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;


/**
 * Displays a TreatmentRecord in a panel
 * @author Nathan Hurley
 * @version 1.0
 */
public class TreatmentRecordPanel extends JPanel{
	/**
	 * Serialization
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The TreatmentRecord used to create the panel
	 */
	private final TreatmentRecord curRecord;
	
	/**
	 * Buttons to update the TreatmentRecord,
	 * create an Invoice, and to view an Invoice
	 */
	private JButton updateRecord, createInvoice, 
		viewInvoice, openOrder, addOrder, removeOrder;
	
	/**
	 * Text fields for the vitals
	 */
	private JTextField patientHeight, weight, temperature, systolicBloodPressure, 
		diastolicBloodPressure, pulse, addOrderText;
	
	/**
	 * Labels for the vitals
	 */
	private JLabel reason, recordDate, heightLabel, weightLabel, 
		tempLabel, bpLabel, pulseLabel, diagnosisLabel, 
		symptomsLabel, notesLabel, orderType, orderName;
	
	/**
	 * Text areas for other fields on the record
	 */
	private JTextArea diagnosis, symptoms, notes;
	
	/**
	 * The current user of the panel
	 */
	private final User curUser;
	
	/**
	 * Panels used to position and organize components
	 */
	private JPanel mainRecInfo, vitals, buttonPanel, otherPanel, 
		holderPan, ordersPan, ordersButtons, doctorNursePanel, mainHolderInfo;
	
	/**
	 * Tabbed pane to add Invoice panels
	 */
	private final UserPanel pan;
	
	/**
	 * List of orders including prescriptions and LabOrders
	 */
	private JList ordersList;
	
	/**
	 * ComboBox with options to select LabOrder or Prescription
	 */
	private JComboBox ordersAddBox, doctorsBox, nursesBox;
	
	/**
	 * List model for ordersList
	 */
	private DefaultListModel ordersModel;
	
	/**
	 * General number of rows
	 */
	private static final int ROWS = 2;
	
	/**
	 * Number of columns in text fields
	 */
	private static final int TEXT_COLS = 20;
	
	/**
	 * Number of columns for the blood pressure fields
	 */
	private static final int BP_COLS = 9;
	
	/**
	 * Width of panels
	 */
	private static final int WIDTH = 750;
	
	/**
	 * Height of panels
	 */
	private static final int HEIGHT = 520;
	
	/**
	 * Constructs a TreatmentRecordPanel around a given treatment and user
	 * @param curRecord TreatmentRecord to build panel around
	 * @param curUser User to build the panel around
	 * @param pan the UserPanel used to add tabs to
	 */
	public TreatmentRecordPanel(TreatmentRecord curRecord, User curUser, UserPanel pan){ // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.constructorsOnlyInvokeFinalMethods
		/*Get Instance Data*/
		this.curRecord = curRecord;
		this.curUser = curUser;
		this.pan = pan;
		final JPanel hold = new JPanel();
		hold.setLayout(new BoxLayout(hold, BoxLayout.Y_AXIS));
		final JScrollPane cur = new JScrollPane(wrap(hold));
		//cur.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		prepareMainRecInfo();
		prepareVitalsPanel();
		prepareOtherPanel();
		prepareOrdersPanels();
		prepareBottomPanels();
		prepareButtonsPanel();
		/*Add components to panel*/
		hold.add(mainHolderInfo);
		hold.add(vitals);
		hold.add(holderPan);
		hold.add(buttonPanel);
		add(cur);
		changeAccess();//edit panel for Patient
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); //set Layout to vertical BoxLayout
		setPreferredSize(new Dimension(WIDTH, HEIGHT));
		hold.setPreferredSize(new Dimension(WIDTH, WIDTH));
		cur.setPreferredSize(new Dimension(WIDTH, HEIGHT));
		prepareOrdersList();
	}
	
	/**
	 * Sets up the bottom panels
	 */
	public final void prepareBottomPanels(){
		holderPan = new JPanel(new GridLayout(1, ROWS));
		holderPan.add(otherPanel);
		holderPan.add(ordersPan);
	}
	
	/**
	 * Sets up the ordersPanel and adds the components to it
	 */
	public final void prepareOrdersPanels(){
		ordersPan = new JPanel();
		ordersPan.setBorder(new TitledBorder("Doctors Orders"));
		ordersPan.setLayout(new GridLayout(1, ROWS));
		ordersModel = new DefaultListModel();
		ordersList = new JList(ordersModel);
		ordersList.addMouseListener(new ListClickListener());
		ordersPan.add(new JScrollPane(ordersList));
		prepareOrdersButtons();
		ordersPan.add(ordersButtons);
	}
	
	/**
	 * Sets up the ComboBox, JButtons, and the
	 * JTextField for adding Orders
	 */
	public final void prepareOrdersButtons(){
		ordersButtons = new JPanel();
		final String[] addOptions = {"Prescription", "Order a Lab"};
		ordersAddBox = new JComboBox(addOptions);
		orderType = new JLabel("Order Type:");
		ordersButtons.setLayout(new GridLayout(0, 1));
		addOrderText = new JTextField();
		addOrderText.setAlignmentX(CENTER_ALIGNMENT);
		addOrderText.addActionListener(new ButtonListener());
		orderName = new JLabel("Order Name");
		openOrder = new JButton("Open Order");
		openOrder.addActionListener(new ButtonListener());
		openOrder.setAlignmentX(CENTER_ALIGNMENT);
		addOrder = new JButton("Add Order");
		addOrder.setAlignmentX(CENTER_ALIGNMENT);
		addOrder.addActionListener(new ButtonListener());
		removeOrder = new JButton("Remove Order");
		removeOrder.setAlignmentX(CENTER_ALIGNMENT);
		removeOrder.addActionListener(new ButtonListener());
		ordersButtons.add(openOrder);
		ordersButtons.add(removeOrder);
		ordersButtons.add(orderType);
		ordersButtons.add(ordersAddBox);
		ordersButtons.add(orderName);
		ordersButtons.add(addOrderText);
		ordersButtons.add(addOrder);
	}
	
	/**
	 * Prepares the panel with the notes, symptoms, and diagnosis
	 */
	public final void prepareOtherPanel(){
		otherPanel = new JPanel();
		otherPanel.setLayout(new BoxLayout(otherPanel, BoxLayout.Y_AXIS));
		otherPanel.setBorder(new TitledBorder("Other Information"));
		diagnosis = new JTextArea(curRecord.getDiagnosis());
		diagnosis.getDocument().addDocumentListener(new TextChangedListener());
		diagnosis.setRows(ROWS);
		diagnosis.setLineWrap(true);
		diagnosis.setWrapStyleWord(true);
		symptoms = new JTextArea(curRecord.getSymptoms());
		symptoms.getDocument().addDocumentListener(new TextChangedListener());
		symptoms.setLineWrap(true);
		symptoms.setRows(ROWS);
		symptoms.setWrapStyleWord(true);
		notes = new JTextArea(curRecord.getNotes());
		notes.getDocument().addDocumentListener(new TextChangedListener());
		notes.setRows(ROWS);
		notes.setLineWrap(true);
		notes.setWrapStyleWord(true);
		otherPanel.add(symptomsLabel);
		otherPanel.add(new JScrollPane(symptoms));
		otherPanel.add(diagnosisLabel);
		otherPanel.add(new JScrollPane(diagnosis));
		otherPanel.add(notesLabel);
		otherPanel.add(new JScrollPane(notes));
	}
	
	/**
	 * Sets up the Doctors Orders list
	 */
	public void prepareOrdersList(){
		for(DoctorsOrder d : curRecord.getDoctorsOrders()){//go throught each Doctors order
			if(d instanceof LabOrder){
				ordersModel.addElement("Lab " + ((LabOrder) d).getName());
			}
			else if(d instanceof Prescription){
				ordersModel.addElement("Prescription " + 
						((Prescription) d).getMedication());
			}
		}
	}
	
	/**
	 * Changes the layout depending on the user
	 */
	public void changeAccess(){
		if(!(this.curUser instanceof Doctor)){//removes ability to place orders if not a doctor
			ordersButtons.remove(addOrder);
			ordersButtons.remove(removeOrder);
			ordersButtons.remove(ordersAddBox);
			ordersButtons.remove(addOrderText);
			ordersButtons.remove(orderType);
			ordersButtons.remove(orderName);
			ordersButtons.remove(openOrder);
		}
		if(this.curUser instanceof Patient || this.curUser instanceof Receptionist){
			patientHeight.setEditable(false);
			weight.setEditable(false);
			temperature.setEditable(false);
			systolicBloodPressure.setEditable(false);
			diastolicBloodPressure.setEditable(false);
			pulse.setEditable(false);
			diagnosis.setEditable(false);
			notes.setEditable(false);
			symptoms.setEditable(false);
			buttonPanel.remove(updateRecord);
			ordersButtons.add(wrap(openOrder));
			doctorsBox.setEnabled(false);
			nursesBox.setEnabled(false);
		}
		else if(this.curUser instanceof Nurse || 
				this.curUser instanceof SystemAdministrator){
			ordersButtons.add(wrap(openOrder));
		}
	}
	
	/**
	 * Sets up the Information panel at the top
	 */
	public void prepareMainRecInfo(){
		mainHolderInfo = new JPanel(new GridLayout(1, ROWS));
		mainRecInfo = new JPanel();
		doctorNursePanel = new JPanel();
		doctorNursePanel.setLayout(new BoxLayout(doctorNursePanel, BoxLayout.Y_AXIS));
		doctorNursePanel.setBorder(new TitledBorder("Attending Doctor and Nurse"));
		final Vector<String> docList = new Vector<String>(); // $codepro.audit.disable declareAsInterface
		docList.add("Select a Doctor");
		for(Doctor d : AccountsController.sharedController().getDoctors()){
			docList.add(d.getName());
		}
		doctorsBox = new JComboBox(docList);
		if(curRecord.getAttendingDoctor() != null){
			doctorsBox.setSelectedItem(curRecord.getAttendingDoctor().getName());
		}
		doctorsBox.addActionListener(new ButtonListener());
		final Vector<String> nurseList = new Vector<String>(); // $codepro.audit.disable declareAsInterface
		nurseList.add("Select a Nurse");
		for(Nurse n : AccountsController.sharedController().getNurses()){
			nurseList.add(n.getName());
		}
		nursesBox = new JComboBox(nurseList);
		if(curRecord.getAttendingNurse() != null){
			nursesBox.setSelectedItem(curRecord.getAttendingNurse().getName());
		}
		nursesBox.addActionListener(new ButtonListener());
		doctorNursePanel.add(new JLabel("Attending Doctor:"));
		doctorNursePanel.add(doctorsBox);
		doctorNursePanel.add(new JLabel("Attending Nurse:"));
		doctorNursePanel.add(nursesBox);
		String strReasons = "";
		for(String s : curRecord.getAppointment().getReason()){
			strReasons += s; // $codepro.audit.disable stringConcatenationInLoop
		}
		reason = new JLabel("<html>Reason for Appointment: " + strReasons + "</html>");
		reason.setPreferredSize(new Dimension(1, 1));
		recordDate = new JLabel("Date: " + curRecord.getDate().toString());
		mainRecInfo.setLayout(new BoxLayout(mainRecInfo, BoxLayout.Y_AXIS));
		mainRecInfo.setBorder(new TitledBorder("Appointment Information"));
		mainRecInfo.add(new JLabel("Patient: " + 
				curRecord.getAppointment().getPatient().getName()));
		mainRecInfo.add(recordDate);
		mainRecInfo.add(reason);
		mainHolderInfo.add(mainRecInfo);
		mainHolderInfo.add(doctorNursePanel);
	}
	
	/**
	 * Prepares the panel containing the vitals
	 */
	public void prepareVitalsPanel(){
		vitals = new JPanel(new GridLayout(0, ROWS));
		vitals.setAlignmentX(CENTER_ALIGNMENT);
		vitals.setBorder(new TitledBorder("Patient's Vitals"));
		heightLabel = new JLabel("Height:");
		weightLabel = new JLabel("Weight:");
		tempLabel = new JLabel("Temperature:");
		bpLabel = new JLabel("Blood Pressure:");
		pulseLabel = new JLabel("Pulse:");
		diagnosisLabel = new JLabel("Diagnosis for Treatment:");
		symptomsLabel = new JLabel("Symptoms:");
		notesLabel = new JLabel("Notes:");
		patientHeight = new JTextField("" + curRecord.getHeight());
		patientHeight.getDocument().addDocumentListener(new TextChangedListener());
		weight = new JTextField("" + curRecord.getWeight());
		weight.getDocument().addDocumentListener(new TextChangedListener());
		temperature = new JTextField("" + curRecord.getTemperature());
		temperature.getDocument().addDocumentListener(new TextChangedListener());
		systolicBloodPressure = new JTextField("" + curRecord.getSystolicBloodPressure());
		systolicBloodPressure.getDocument().addDocumentListener(
				new TextChangedListener());
		diastolicBloodPressure = new JTextField("" + 
				curRecord.getDiastolicBloodPressure());
		diastolicBloodPressure.getDocument().addDocumentListener(
				new TextChangedListener());
		pulse = new JTextField("" + curRecord.getPulse());
		pulse.getDocument().addDocumentListener(new TextChangedListener());
		weight.setColumns(TEXT_COLS);
		patientHeight.setColumns(TEXT_COLS);
		pulse.setColumns(TEXT_COLS);
		temperature.setColumns(TEXT_COLS);
		systolicBloodPressure.setColumns(BP_COLS);
		diastolicBloodPressure.setColumns(BP_COLS);
		final JPanel bp = new JPanel();
		bp.setLayout(new BoxLayout(bp, BoxLayout.X_AXIS));
		/*Add objects to the panel*/
		vitals.add(heightLabel);
		vitals.add(wrap(patientHeight));
		vitals.add(weightLabel);
		vitals.add(wrap(weight));
		vitals.add(tempLabel);
		vitals.add(wrap(temperature));
		vitals.add((bpLabel));
		bp.add(systolicBloodPressure);
		bp.add(new JLabel(" / "));
		bp.add(diastolicBloodPressure);
		vitals.add(wrap(bp));
		vitals.add(pulseLabel);
		vitals.add(wrap(pulse));
	}
	
	/**
	 * Wraps a component in a JPanel
	 * @param comp the component to wrap
	 * @return the wrapped component
	 */
	public static JComponent wrap(JComponent comp){
        final JPanel panel = new JPanel();
        panel.add(comp);
        panel.setAlignmentX(RIGHT_ALIGNMENT);
        return panel;
    }
	
	/**
	 * Sets up the buttons panel
	 */
	public void prepareButtonsPanel(){
		buttonPanel = new JPanel();
		updateRecord = new JButton("Save Changes");
		updateRecord.addActionListener(new ButtonListener()); //add Listener to Button
		createInvoice = new JButton("Create Invoice");
		createInvoice.addActionListener(new ButtonListener());
		viewInvoice = new JButton("View Invoice");
		viewInvoice.addActionListener(new ButtonListener());
		buttonPanel.add(updateRecord);
		if(curRecord.getInvoice() == null){
			if(!(curUser instanceof Patient)){
				buttonPanel.add(createInvoice);
			}
			else{
				buttonPanel.add(new JLabel("The Invoice for " +
						"this treatment is currently not available"));
			}
		}
		else{
			buttonPanel.add(viewInvoice);
		}
	}
	
	/**
	 * Button Listener for the TreatmentRecords
	 * @author Nathan Hurley
	 * @version 1.0
	 *
	 */
	private class ButtonListener implements ActionListener{
		/**
		 * Treatment record controller
		 */
		private final TreatmentRecordController treatController = 
			new TreatmentRecordController(curRecord);
		
		/**
		 * Executes when a button is clicked
		 * @param e the event that has occurred
		 */
		public void actionPerformed(ActionEvent e){
			if(e.getSource() == updateRecord){
				saveRecord();
			}
			else if(e.getSource() == createInvoice){
				createInvoice();
			}
			else if(e.getSource() == viewInvoice){
				pan.addNewTab("Invoice", 
						new InvoicePanel(curRecord.getInvoice(), curUser));
			}
			else if(e.getSource() == openOrder){
				openOrderFromList();
			}
			else if(e.getSource() == addOrder || e.getSource() == addOrderText){
				addOrder();
			}
			else if(e.getSource() == removeOrder){
				if(ordersList.getSelectedIndex() != -1){
					treatController.removeOrder(ordersList.getSelectedIndex());
					ordersModel.removeElementAt(ordersList.getSelectedIndex());
				}
			}
			else if(e.getSource() == doctorsBox){
				attendingDoc();
			}
			else if(e.getSource() == nursesBox){
				attendingNurse();
			}
		}
		
		/**
		 * Adds a doctors order
		 */
		public void addOrder(){
			final int chosenBox = ordersAddBox.getSelectedIndex();
			String name = addOrderText.getText();
			if(chosenBox == 0){
				if(name.trim().equals("")){
					name = "Medicine Name";
				}
				ordersModel.addElement("Prescription - " + name);
				curRecord.getDoctorsOrders().add(new Prescription("", name, "", ""));
			}
			else{
				if(name.trim().equals("")){
					name = "Lab Name";
				}
				ordersModel.addElement("Lab - " + name);
				treatController.saveOrder(new LabOrder("", name, "", 0));
			}
			addOrderText.setText("");
		}
		
		/**
		 * Sets the attending doctor
		 */
		public void attendingDoc(){
			Doctor foundDoc = null;
			for(Doctor d : AccountsController.sharedController().getDoctors()){
				if(d.getName().equals(doctorsBox.getSelectedItem())){
					foundDoc = d;
					break;
				}
			}
			curRecord.setAttendingDoctor(foundDoc);
		}
		
		/**
		 * Sets the attending nurse
		 */
		public void attendingNurse(){
			Nurse foundNurse = null;
			for(Nurse n : AccountsController.sharedController().getNurses()){
				if(n.getName().equals(nursesBox.getSelectedItem())){
					foundNurse = n;
					break;
				}
			}
			curRecord.setAttendingNurse(foundNurse);
		}
		
		/**
		 * Creates an Invoice
		 */
		public void createInvoice(){
			final Invoice toAddInvoice = new Invoice(curRecord);
			curRecord.setInvoice(toAddInvoice);
			pan.addNewTab("Invoice", new EditInvoicePanel(toAddInvoice, curUser));
			buttonPanel.remove(createInvoice);
			buttonPanel.add(viewInvoice);
			updateUI();
		}
		
		/**
		 * @return indicates location of listener
		 */
		public String toString(){
			return "ButtonListener for TreatmentRecordPanel";
		}
	}
	
	/**
	 * Saves the TreatmentRecord for the panel
	 */
	public void saveRecord(){
		curRecord.setHeight(patientHeight.getText());
		curRecord.setWeight(weight.getText());
		curRecord.setTemperature(temperature.getText());
		curRecord.setSystolicBloodPressure(systolicBloodPressure.getText());
		curRecord.setDiastolicBloodPressure(diastolicBloodPressure.getText());
		curRecord.setPulse(pulse.getText());
		curRecord.setSymptoms(symptoms.getText());
		curRecord.setDiagnosis(diagnosis.getText());
		curRecord.setNotes(notes.getText());
	}
	
	/**
	 * Opens a Doctors order
	 */
	public void openOrderFromList(){
		if(ordersList.getSelectedIndex() != -1){
			final DoctorsOrder selectedOrder = curRecord.getDoctorsOrders()
			.get(ordersList.getSelectedIndex());
			if(selectedOrder instanceof LabOrder){//open a LabOrder
				final String orderName = (String) ordersList.getSelectedValue();
				final String tabName = orderName.substring(4); // $codepro.audit.disable numericLiterals
				pan.addNewTab(tabName, new LabOrderPanel((LabOrder) selectedOrder, pan));
			}
			else{//open a Prescription order
				final String orderName = (String) ordersList.getSelectedValue();
				final String tabName = orderName.substring(13); // $codepro.audit.disable numericLiterals
				pan.addNewTab(tabName, new 
						PrescriptionPanel((Prescription) selectedOrder, pan));
			}
		}
	}
	
	/**
	 * Saves the information when any of it is changed
	 * @author Nathan Hurley
	 * @version 1.0
	 */
	private class TextChangedListener implements DocumentListener{
		/**
		 * Not used but needed for interface
		 * @param e the even that has occurred
		 */
		public void changedUpdate(DocumentEvent e){ // $codepro.audit.disable emptyMethod
		}
		
		/**
		 * Used for when text is deleted from text fields
		 * @param e the event that has occurred
		 */
		public void removeUpdate(DocumentEvent e){
			saveRecord();
		}
		
		/**
		 * Saves data when text is added to fields
		 * @param e the even that has occurred
		 */
		public void insertUpdate(DocumentEvent e){
			saveRecord();
		}
		
		/**
		 * @return location of this listener
		 */
		public String toString(){
			return "Document Listener for TreatmentRecordPanel";
		}
	}
	
	/**
	 * Listens for clicks from the user
	 * @author Nathan Hurley
	 * @version 1.0
	 */
	private class ListClickListener extends MouseAdapter{
		/**
		 * Represents a double click
		 */
		private static final int DOUBLE_CLICK = 2;
		
		/**
		 * Changes panels when user double clicks an element in one of the
		 * JLists
		 * @param evt mouse even that has occurred
		 */
		public void mouseClicked(MouseEvent evt) {
	        	if (evt.getClickCount() == DOUBLE_CLICK) {
	        		openOrderFromList();
	        	}
	    }
		
		/**
		 * @return String indicating listeners location
		 */
		public String toString(){
			return "List Listener for the TreatmentRecordPanel";
		}
	}
	
	/**
	 * @return the panels TreatmentRecord
	 */
	public TreatmentRecord getRecord(){
		return curRecord;
	}
	
	/**
	 * Updates panel for when tab is clicked
	 */
	public void updatePanel(){
		ordersModel.clear();
		prepareOrdersList();
	}
	
	/**
	 * @param o the object to compare to
	 * @return true if this equals o and false otherwise
	 */
	public boolean equals(Object o){
		if(this == o){
			return true;
		}
		if(o instanceof TreatmentRecordPanel){
			final TreatmentRecordPanel rec = (TreatmentRecordPanel) o;
			if(rec.getRecord().equals(this.getRecord())){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * @return hash code for the corresponding TreatmentRecord
	 */
	public int hashCode(){
		return curRecord.hashCode();
	}
	
	/**
	 * @return String representation of the TreatmentRecord for this panel
	 */
	public String toString(){
		return curRecord.toString() + 
		" is the current record for the panel";
	}
}
